package society.framework.core;

import java.util.HashMap;

import society.framework.core.communication.Message;
import society.framework.core.communication.MessageRouter;
import society.framework.core.xml.XMLAutomaton;

/**
 * Abstract class which manages a society of automatons. It performs adding/removing/starting/stopping
 * automatons and persistence operations. It is also responsible with sending and receiving automatons
 * from the remote application.
 * @author Micu Andrei
 *
 */
public abstract class SocietyManager {	
	protected HashMap<String, BaseAutomaton> automatons;
	
	protected AutomataTransferAutomaton automataTransferAutomaton;
	
	public SocietyManager(){
		this.automatons = new HashMap<String, BaseAutomaton>();
	}
	
	public void startAutomataTransfer(){
		this.automataTransferAutomaton = new AutomataTransferAutomaton(this);
		MessageRouter.getInstance().subscribe("AutomataTransferAutomaton", this.automataTransferAutomaton);
		this.automataTransferAutomaton.start();
		MessageRouter.getInstance().sendLocal(SocietyInformation.UI_MANAGER_NAME,
                new Message("SocietyManager", "green:Automata transfer started."));
	}
	
	public void stopAutomataTransfer(){
		this.automataTransferAutomaton.stopSafely(new Callback() {
			
			public void handleMethod() {
				MessageRouter.getInstance().sendLocal(SocietyInformation.UI_MANAGER_NAME,
		                new Message("SocietyManager", "green:Automata transfer successfully stopped."));
			}
		});
	}
	
	/**
	 * Returns the automaton that identifies with the name given as parameter
	 * @param name - the name which this automaton identifies with
	 * @return - reference to the automaton or null if there is no automaton with that name
	 */
	public BaseAutomaton getAutomaton(String name){
		return automatons.get(name);
	}
	
	/**
	 * Adds the automaton to the society. It will be identified by name parameter.
	 * @param name - the name which this automaton identifies with
	 * @param automaton - reference to the automaton
	 * @return - true if operation succeeded
	 */
	public boolean addAutomaton(String name, BaseAutomaton automaton){
		return automatons.put(name, automaton)!=null;
	}
	
	/**
	 * Removes the automaton with the name given as parameter.
	 * @param name - the name which this automaton identifies with
	 * @return - true if operation succeeded
	 */
	public boolean removeAutomaton(String name){
		return automatons.remove(name)!=null;
	}
	
	/**
	 * Removes all automatons from the society.
	 */
	public void clear(){
		automatons.clear();
	}
	
	/**
	 * Starts all the automatons in the society.
	 */
	public void startAutomatons(){
		for(BaseAutomaton automaton : automatons.values()){
			automaton.start();
		}
	}
	
	/**
	 * Stops all the automatons in the society.
	 */
	public void stopAutomatons(){
		for(BaseAutomaton automaton : automatons.values()){
			automaton.stopSafely();
		}
	}

	/**
	 * Saves the state of the society.
	 * @return true if saving was successful or false otherwise
	 */
	public abstract boolean saveState();

	/**
	 * Loads the state of the society.
	 * @return - true if loading was successful or false otherwise
	 */
	public abstract boolean loadState();
	
	/**
	 * Sends an XML automaton in the society with the specified name on the remote application.
	 * @param name - name with which the automaton is identified
	 * @return - false if automaton was not found or if it isn't an XMLAutomaton, true otherwise
	 */
	public boolean sendXMLAutomatonToRemoteApplication(final String name){
		final BaseAutomaton automaton = automatons.get(name);
		if(automaton!= null && automaton instanceof XMLAutomaton){
			((XMLAutomaton)automaton).saveSnapshot();
			automaton.stopSafely(new Callback(){

				public void handleMethod() {
					automataTransferAutomaton.addMessage(new Message("", automaton));
				}
				
			});
			return true;
		}
		return false;
	}
	
	/**
	 * Sends a request to the remote application to serialize and sends back the automaton with the specified name.
	 * @param name - name with which the automaton is identified
	 */
	public void requestXMLAutomatonFromRemoteApplication(final String name){
		MessageRouter.getInstance().sendRemote("AutomataTransferAutomaton", new Message("AutomataTransferAutomaton", name));
	}
}
